package com.huangyi.chatservice.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huangyi.adminapi.appuser.domain.dto.AppUserDTO;
import com.huangyi.adminapi.appuser.domain.vo.AppUserVo;
import com.huangyi.adminapi.appuser.feign.AppUserFeignClient;
import com.huangyi.chatservice.domain.dto.*;
import com.huangyi.chatservice.domain.entity.Session;
import com.huangyi.chatservice.domain.vo.MessageVO;
import com.huangyi.chatservice.domain.vo.SessionAddResVO;
import com.huangyi.chatservice.domain.vo.SessionGetResVO;
import com.huangyi.chatservice.mapper.SessionMapper;
import com.huangyi.chatservice.service.ChatCacheService;
import com.huangyi.chatservice.service.ISessionService;
import com.huangyi.commondomain.domain.R;
import com.huangyi.commondomain.domain.ResultCode;
import com.huangyi.commondomain.exception.ServiceException;
import com.huangyi.commonsecurity.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SessionServiceImpl implements ISessionService {
    @Autowired
    private ChatCacheService chatCacheService;

    @Autowired
    private SessionMapper sessionMapper;

    @Autowired
    private AppUserFeignClient appUserFeignClient;

    @Autowired
    private TokenService tokenService;

    @Override
    public SessionAddResVO add(SessionAddReqDTO sessionAddReqDTO) {

        Long loginUserId = tokenService.getLoginUser().getUserId();
        Long userId1 = sessionAddReqDTO.getUserId1();
        Long userId2 = sessionAddReqDTO.getUserId2();

        // 对 userId 排序
        boolean isSwapped = userId1 > userId2;
        if (isSwapped) {
            Long tmp = userId1;
            userId1 = userId2;
            userId2 = tmp;
        }

        // Redis 没有则查询 MySQL？ TODO
        // 查询会话(MySQL), 不用先查 redis 吗？ TODO
        Session session = sessionMapper.selectOne(
                new LambdaQueryWrapper<Session>()
                        .eq(Session::getUserId1, userId1)
                        .eq(Session::getUserId2, userId2)
        );
        if(session != null) {
            // 如果MySQL 查询到了返回缓存
            // 先查询 Redis 缓存, 有就返回
            SessionStatusDetailDTO sessionDTO = chatCacheService.getSessionDTOByCache(session.getId());
            assert null != sessionDTO;
            SessionAddResVO resVO = new SessionAddResVO();
            resVO.setSessionId(session.getId());
            resVO.setLoginUser(
                    sessionDTO.getFromUser(loginUserId).getUser().convertToVO());
            resVO.setOtherUser(
                    sessionDTO.getToUser(loginUserId).getUser().convertToVO());
            return resVO;
        }

        // 新建会话插入 MySQL
        Session newSession = new Session();
        newSession.setUserId1(userId1);
        newSession.setUserId2(userId2);
        sessionMapper.insert(newSession);

        // 查询 user_app 表对应的用户, 赋值到 statusDetailDTO.userInfo 中
        R<List<AppUserVo>> r = appUserFeignClient.list(Arrays.asList(userId1, userId2));
        if(r == null || r.getData() == null || r.getCode() != ResultCode.SUCCESS.getCode()) {
            log.error("新增会话时，用户未查询到，新增失败! userId1:{}, userId2:{}", userId1, userId2);
            throw new ServiceException("新增会话时，用户未查询到！");
        }

        // 把会话详情设置到 Redis 缓存, 涉及到 user_app
        Map<Long, AppUserDTO> userVoMap = r.getData().stream()
                .map(appUserVo ->{
                    AppUserDTO appUserDTO = new AppUserDTO();
                    BeanUtils.copyProperties(appUserVo, appUserDTO);
                    return appUserDTO;
                }).collect(Collectors.toMap(AppUserDTO::getUserId, Function.identity()));

        SessionStatusDetailDTO statusDetailDTO = new SessionStatusDetailDTO();
        statusDetailDTO.setSessionId(newSession.getId());
        SessionStatusDetailDTO.UserInfo userInfo1 = new SessionStatusDetailDTO.UserInfo();
        userInfo1.setUser(userVoMap.get(userId1));
        statusDetailDTO.setUser1(userInfo1);

        SessionStatusDetailDTO.UserInfo userInfo2 = new SessionStatusDetailDTO.UserInfo();
        userInfo2.setUser(userVoMap.get(userId2));
        statusDetailDTO.setUser2(userInfo2);
        chatCacheService.cacheSessionDTO(newSession.getId(),statusDetailDTO);

        // 构造并返回
        SessionAddResVO sessionAddResVO = new SessionAddResVO();
        sessionAddResVO.setSessionId(newSession.getId());
        sessionAddResVO.setLoginUser(
                statusDetailDTO.getFromUser(loginUserId).getUser().convertToVO()
        );
        sessionAddResVO.setOtherUser(
                statusDetailDTO.getToUser(loginUserId).getUser().convertToVO()
        );

        return sessionAddResVO;
    }

    /**
     * 查询俩用户的会话信息
     *
     * @param sessionGetReqDTO
     * @return
     */
    @Override
    public SessionGetResVO get(SessionGetReqDTO sessionGetReqDTO) {
        SessionGetResVO sessionGetResVO = new SessionGetResVO();

        Long loginUserId = tokenService.getLoginUser().getUserId();
        Long userId1 = sessionGetReqDTO.getUserId1();
        Long userId2 = sessionGetReqDTO.getUserId2();

        // 对 userId 排序
        boolean isSwapped = userId1 > userId2;
        if (isSwapped) {
            Long tmp = userId1;
            userId1 = userId2;
            userId2 = tmp;
        }

        // Redis 没有则查询 MySQL？ TODO
        // 查询会话(MySQL), 不用先查 redis 吗？ TODO
        Session session = sessionMapper.selectOne(
                new LambdaQueryWrapper<Session>()
                        .eq(Session::getUserId1, userId1)
                        .eq(Session::getUserId2, userId2)
        );

        // 不存在返回空
        if(session == null) {
            return sessionGetResVO;
        }

        // 存在构造并返回
        SessionStatusDetailDTO sessionDTO = chatCacheService.getSessionDTOByCache(session.getId());
        if(sessionDTO == null) {
            throw new ServiceException("数据库与缓存用户 id 不一致！");
        }

        sessionGetResVO.setSessionId(session.getId());
        if (null != sessionDTO.getLastMessageDTO()) {
            MessageVO messageVO = new MessageVO();
            BeanUtils.copyProperties(sessionDTO.getLastMessageDTO(), messageVO);
            sessionGetResVO.setLastMessageVO(messageVO);
        }
        if (null != sessionDTO.getLastSessionTime()) {
            sessionGetResVO.setLastSessionTime(sessionDTO.getLastSessionTime());
        }
        sessionGetResVO.setOtherUser(
                sessionDTO.getToUser(loginUserId).getUser().convertToVO()
        );
        sessionGetResVO.setNotVisitedCount(sessionDTO.getFromUser(loginUserId).getNotVisitedCount());
        return sessionGetResVO;
    }

    @Override
    public List<SessionGetResVO> list(SessionListReqDTO sessionListReqDTO) {
        // 1. 查询当前登录用户下的已经聊过的会话id列表（按照会话的最后时间排序）
        // 目标：必须聊过天才能查到
        // 用户下的会话id列表什么时候存？ 不是在创建会话时存，而是在第一次发消息聊天才会存。
        Long loginUserId = tokenService.getLoginUser().getUserId();
        Set<Long> sessionIds = chatCacheService.getUserSessionsByCache(loginUserId);
        if (CollectionUtils.isEmpty(sessionIds)) {
            return Arrays.asList();
        }

        // 2. 查询会话状态详情，并构造结果
        return sessionIds.stream()
                .map(sessionId -> chatCacheService.getSessionDTOByCache(sessionId))
                .filter(sessionDetail -> sessionDetail != null && sessionDetail.getLastMessageDTO() !=null)
                .map(sessionDetail -> {
                    SessionGetResVO resVO = new SessionGetResVO();
                    MessageVO messageVO = new MessageVO();
                    BeanUtils.copyProperties(sessionDetail.getLastMessageDTO(), messageVO);
                    resVO.setLastMessageVO(messageVO);
                    resVO.setSessionId(sessionDetail.getSessionId());
                    resVO.setLastSessionTime(sessionDetail.getLastSessionTime());
                    resVO.setOtherUser(
                            sessionDetail.getToUser(loginUserId).getUser().convertToVO());
                    resVO.setNotVisitedCount(
                            sessionDetail.getFromUser(loginUserId).getNotVisitedCount());
                    return resVO;
                }).collect(Collectors.toList());

    }


    @Override
    public Boolean hasHouse(SessionHouseReqDTO sessionHouseReqDTO) {

        // 查会话详细信息（Redis）
        SessionStatusDetailDTO sessionDTO =
                chatCacheService.getSessionDTOByCache(sessionHouseReqDTO.getSessionId());
        if (null == sessionDTO) {
            throw new ServiceException("会话id有误，不存在其会话信息！");
        }

        Set<Long> houseIds = sessionDTO.getHouseIds();
        if (CollectionUtils.isEmpty(houseIds)) {
            return false;
        }

        return houseIds.contains(sessionHouseReqDTO.getHouseId());

    }
}
